package com.trussell.genesis;

import java.util.ArrayList;
import java.util.concurrent.Callable;

import com.jme.bounding.BoundingBox;
import com.jme.input.MouseInputListener;
import com.jme.intersection.PickResults;
import com.jme.intersection.TrianglePickResults;
import com.jme.light.DirectionalLight;
import com.jme.light.Light;
import com.jme.math.Ray;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.pass.ShadowedRenderPass;
import com.jme.renderer.pass.ShadowedRenderPass.LightingMethod;
import com.jme.scene.TriMesh;
import com.jme.scene.state.FogState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.WireframeState;
import com.jme.system.DisplaySystem;
import com.jme.system.canvas.SimplePassCanvasImpl;
import com.jme.util.GameTaskQueue;
import com.jme.util.GameTaskQueueManager;
import com.jmex.awt.input.AWTMouseInput;
import com.jmex.effects.glsl.BloomRenderPass;
import com.jmex.effects.water.WaterRenderPass;
import com.jmex.terrain.TerrainBlock;
import com.trussell.genesis.editor.TerrainMarker;
import com.trussell.genesis.terrainmanipulation.TerrainEditUtil;
import com.trussell.genesis.util.ConfigManager;
import com.trussell.genesis.windowutils.toolbars.TerrainEditToolBar;

public class GenesisImplementor extends SimplePassCanvasImpl {

	private static GenesisImplementor instance;

	public static GenesisImplementor getInstance() {
		if (instance == null)
			instance = new GenesisImplementor(ConfigManager.getInstance()
					.getDefaultWidth(), ConfigManager.getInstance()
					.getDefaultHeight());
		return instance;
	}

	private GenesisImplementor(int width, int height) {
		super(width, height);
	}

	public enum Mode {
		NONE, TERRAIN_EDIT, EFFECTS_EDIT
	};

	public enum TerrainEditMode {
		NONE, RAISE, LOWER, SMOOTH, NOISE
	};

	public enum TextureEditMode {
		BASE, PAINT, ERASE
	};

	private Mode masterMode;
	private TerrainEditMode terrainEditMode;
	private TextureEditMode textureEditMode;

	// TODO implement these.
	private LightState lightState; // lights
	private FogState fogState; // fog
	private TextureState texState; // texture
	private WireframeState wireState; // wireframe

	private ShadowedRenderPass shadowPass; // shadows
	private WaterRenderPass waterPass; // water
	private BloomRenderPass bloomPass; // bloom

	// TODO fix this
	private TerrainMarker indicatorRing;
	private PickResults results = new TrianglePickResults();
	private int radius = 1;

	// List of light sources and reference to the terrain block.
	private ArrayList<Light> lights = new ArrayList<Light>();
	private TerrainBlock tBlock;
	private int mapSize;

	// Map Information
	public static int tileSize = 16; // maps are 16x16 per "size unit"
	public static int tileScale = 5; // stretch them out 5 times to keep

	// textures

	public void simpleSetup() {
		lightState = DisplaySystem.getDisplaySystem().getRenderer()
				.createLightState(); // resets the light state
		lightState.setEnabled(true);

		DirectionalLight dr = new DirectionalLight();
		dr.setEnabled(true);
		dr.setDirection(new Vector3f(-1, -1, -1));
		dr.setShadowCaster(true);
		
		lightState.attach(dr);
		lightState.setGlobalAmbient(new ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f));
		
		

		wireState = renderer.createWireframeState();
		wireState.setEnabled(false);

		rootNode.setRenderState(lightState);
		rootNode.setRenderState(wireState);

		cam.setLocation(new Vector3f(-10, 10, 10));
		cam.lookAt(new Vector3f(0, 0, 0), new Vector3f(0, 1, 0));

		/*
		 * DO STATES HERE
		 */
		shadowPass = new ShadowedRenderPass();
		shadowPass.add(rootNode);
		shadowPass.addOccluder(rootNode);
		shadowPass.setLightingMethod(LightingMethod.Additive);
		shadowPass.setRenderShadows(true);

		manager.add(shadowPass);

		setMode(Mode.NONE, true);
	}

	public void simpleUpdate() {
		if (masterMode == Mode.TERRAIN_EDIT
				&& !AWTMouseInput.get().isButtonDown(2)) {
			doTerrainEdits();
		} else {
			if (indicatorRing != null) {
				indicatorRing.removeFromParent();
				indicatorRing = null;
			}
		}
	}

	public void reset() {
		Callable<Void> exe = new Callable<Void>() {
			@Override
			public Void call() throws Exception {
				DisplaySystem.getDisplaySystem().reset();
				DisplaySystem.getDisplaySystem().getRenderer().reinit(
						Genesis.getInstance().getJMECanvasSize().width,
						Genesis.getInstance().getJMECanvasSize().height);
				doSetup();
				return null;
			}
		};
		GameTaskQueueManager.getManager().getQueue(GameTaskQueue.UPDATE)
				.enqueue(exe);
	}

	/**
	 * Changes the mode of operation.
	 * 
	 * @param m
	 *            : the mode to change to (GenesisImplementor.Mode)
	 * @param internal
	 *            : determines if the command originated from within this class.
	 *            If it did, we update Genesis' main menues. If not, we leave
	 *            them be because we know the command came from them.
	 */
	public void setMode(Mode m, boolean internal) {
		masterMode = m;
		if (internal)
			Genesis.getInstance().setMode(m);
	}

	public void setTerrainEditMode(TerrainEditMode m) {
		terrainEditMode = m;
	}

	public void setTextureEditMode(TextureEditMode m) {
		textureEditMode = m;
	}

	public Mode getMode() {
		return masterMode;
	}

	public int getMapSize() {
		return mapSize;
	}

	public void setMapSize(int n) {
		mapSize = n;

		if (tBlock != null) {
			tBlock.removeFromParent();
		}

		tBlock = new TerrainBlock("map", (mapSize * tileSize), new Vector3f(
				tileScale, 1, tileScale),
				new float[tileSize * n * tileSize * n], new Vector3f(0, 0, 0));

		tBlock.setModelBound(new BoundingBox());
		tBlock.updateModelBound();

		rootNode.attachChild(tBlock);
		rootNode.updateRenderState();
	}

	public void doTerrainEdits() {
		if (indicatorRing == null) {
			indicatorRing = new TerrainMarker("marker", 30, TerrainEditToolBar
					.getInstance().getRadius(), .25f, ColorRGBA.green);
			rootNode.attachChild(indicatorRing);
			rootNode.updateRenderState();
		}

		Vector2f screenPos = new Vector2f(AWTMouseInput.get().getXAbsolute(),
				AWTMouseInput.get().getYAbsolute());

		Ray ray = new Ray();
		DisplaySystem.getDisplaySystem().getPickRay(screenPos, true, ray);

		results.clear();
		rootNode.findPick(ray, results);

		Vector3f loc = new Vector3f();
		Vector3f[] vertex = new Vector3f[3];
		boolean foundMeshHit = false;

		for (int pickIndex = 0; pickIndex < results.getNumber(); pickIndex++) {
			if (results.getPickData(pickIndex).getTargetMesh() instanceof TerrainBlock) {
				ArrayList<Integer> a = results.getPickData(pickIndex)
						.getTargetTris();
				TriMesh mesh = (TriMesh) results.getPickData(pickIndex)
						.getTargetMesh();
				for (Integer i : a) {
					mesh.getTriangle(i, vertex);
					foundMeshHit = (ray.intersectWhere(vertex[0].addLocal(mesh
							.getWorldTranslation()), vertex[1].addLocal(mesh
							.getWorldTranslation()), vertex[2].addLocal(mesh
							.getWorldTranslation()), loc));

					if (foundMeshHit) {
						indicatorRing.reset(ColorRGBA.blue, TerrainEditToolBar
								.getInstance().getRadius());
						indicatorRing.update(loc, tBlock);
						rootNode.updateRenderState();
						if (AWTMouseInput.get().isButtonDown(0)) {
							switch (terrainEditMode) {
							case RAISE:
								TerrainEditUtil.raiseLowerTerrain(
										(TerrainBlock) mesh, loc,
										TerrainEditToolBar.getInstance()
												.getRadius(),
										TerrainEditToolBar.getInstance()
												.getIntensity(), tpf);
								break;
							case LOWER:
								TerrainEditUtil.raiseLowerTerrain(
										(TerrainBlock) mesh, loc,
										TerrainEditToolBar.getInstance()
												.getRadius(),
										TerrainEditToolBar.getInstance()
												.getIntensity() * -1, tpf);
								break;
							case SMOOTH:
								TerrainEditUtil.smoothTerrain(
										(TerrainBlock) mesh, loc,
										TerrainEditToolBar.getInstance()
												.getRadius(),
										TerrainEditToolBar.getInstance()
												.getIntensity(), tpf);
							}
						}
					}
				}
			}
		}
	}

	public void setLighting(boolean b) {
		lightState.setEnabled(b);
		rootNode.updateRenderState();
	}

	public void setWireframe(boolean b) {
		wireState.setEnabled(b);
		rootNode.updateRenderState();
	}

	public void setShadows(boolean b) {
		shadowPass.setEnabled(b);
	}

	public TriMesh getTerrain() {
		return tBlock;
	}
}
